/*
 * Copyright 2002-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.framework;

import org.springframework.aop.SpringProxy;
import org.springframework.core.NativeDetector;

import java.io.Serializable;
import java.lang.reflect.Proxy;

/**
 * 默认的aop代理工厂实现，能够创建jdk 和cglib 两种代理对象。
 * 如果对于给定的AdviseSupport实例满足一下任一条件，那么就会创建cglib的代理。
 * <p>
 * 1. optimize 属性已经设置
 * 2. 设置了proxyTargetClass属性
 * 3. 没有指定代理接口
 * <p>
 * 一般情况下，都是通过指定proxyTargetClass属性强制使用cglib的代理
 * 通过指定一个或者多个接口去使用jdk的动态代理
 */
@SuppressWarnings("serial")
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

	/**
	 * @param config 就是我们的ProxyFactory对象，ProxyFactory他是一个配置管理对象
	 *               保存着创建代理对象所有的生产资料。
	 * @return 返回一个AOP的代理对象
	 * @throws AopConfigException 如果某些不期望我们修改的配置被修改，就会抛出异常
	 */
	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		/**
		 * 条件一：暂且不管
		 * 条件二：true 表示强制使用cglib代理，
		 * 条件三：true 表示被代理对象没有实现任何接口没有办法使用jdk的动态代理，只能使用cglib的动态代理
		 */
		if (!NativeDetector.inNativeImage() && //该条件不需要考虑

				(
						config.isOptimize() ||  //设置了这个属性，那么就是强制使用cglib的动态代理
								config.isProxyTargetClass() ||  //设置了这个属性，那么就是强制使用cglib的动态代理
								hasNoUserSuppliedProxyInterfaces(config) //判断被代理对象有没有实现接口，没有实现接口，那还用锤子jdk的动态代理
				)
		) {
			//走到这里的话，很大程度上就已经会使用cglib的动态代理
			//获取目标对象的类型，为空的话肯定没法继续往下走了，直接异常中断
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
			}
			//目标对象是一个接口 或者 已经是一个被代理过得类型（此时是多重代理） ，只能使用jdk的动态代理
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			//走cglib的动态代理
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			//执行到这里的情况 ： 实现了接口,大多数情况我们都是面向接口编程，走这里
			return new JdkDynamicAopProxy(config);
		}
	}

	/**
	 * 确定提供的AdvisedSupport是否只指定了SpringProxy接口（或根本没有指定代理接口）。
	 * 说白了就是判断你的目标对象有没有实现的接口，能不能使用jdk的动态代理
	 * @param config
	 * @return
	 */
	private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
		Class<?>[] ifcs = config.getProxiedInterfaces();
		return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
	}

}
